package com.duoduo.commonn.redis.factory;


import com.duoduo.common.api.constants.Constant;
import com.duoduo.common.api.constants.LogConstant;
import com.duoduo.common.api.domain.DelayExecuteMessage;
import com.duoduo.common.api.exception.lock.*;
import com.duoduo.common.api.help.*;
import com.duoduo.common.api.myinterface.LockOperator;
import com.duoduo.common.api.myinterface.tracer.RedisTracer;
import com.duoduo.common.api.spi.extension.ExtensionLoader;
import com.duoduo.common.api.tuple.Tuple;
import com.duoduo.common.api.tuple.TwoTuple;
import com.duoduo.commonn.redis.handle.TraceHandler;
import com.duoduo.commonn.redis.myinterface.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**同一jvm里只能有一个线程获取锁；多个jvm每个jvm最多一个线程可以获取锁。
 * 可重入指同一个jvm里拥有相同requestId的同一个线程才可重入；不同的jvm里需要拥有同一个requestId就可以重入
 * 测试场景：
 * 业务代码执行12s，锁的获取超时时间5s
 *
 * 1、一个jvm：a线程加锁成功，b线程2s后开始请求锁，5s后b线程提示加锁失败，12s后a线程执行成功
 * 2、一个jvm：a线程加锁成功，b线程10s后开始请求锁，12s后a线程执行成功，之后b线程获取锁，12s后b线程执行成功
 * 3、一个jvm：a线程加锁成功，b线程2s后开始请求锁，c线程4s后开始加锁，结果是b、c都失败，a执行成功
 * 4、一个jvm：a线程加锁成功，b线程2s后开始请求锁，c线程10s后开始加锁，结果是b失败，a、c执行成功
 *
 * 1、两个jvm(a线程属于一个jvm；c线程属于一个jvm)：a线程加锁成功，c线程2s后开始请求锁，5s后c线程提示加锁失败，12s后a线程执行成功
 * 2、两个jvm(a线程属于一个jvm；c线程属于一个jvm)：a线程加锁成功，c线程10s后开始请求锁，12s后a线程执行成功，之后c线程获取锁，12s后c线程执行成功
 * 3、两个jvm(a线程属于一个jvm；c线程、d线程属于一个jvm)：a线程加锁成功，c、d线程2s后开始请求锁，c线程不断轮询获取锁，d线程入队，最后2个线程都失败
 * 4、两个jvm(a线程属于一个jvm；c线程、d线程属于一个jvm)：a线程加锁成功，c线程2s后开始请求锁，c线程不断轮询获取锁，d线程10s后开始加锁，d线程入队，最后c线程失败，d线程唤醒后成功
 *
 * 重入(假设两个requestId分别是requestId1、requestId2)
 * 1、一个jvm：a线程重入两次(都用requestId1)
 * 2、两个jvm(a、b线程属于一个jvm；c线程属于一个jvm)：a线程(requestId1)加锁成功，10s后b线程(requestId2)加锁，然后c线程(requestId1)加锁，结果是c线程可以重入，但是a线程释放锁后，b线程立刻加锁成功，c线程释放锁时失败
 * 2、两个jvm(a、b线程属于一个jvm；c线程属于一个jvm)：a线程(requestId1)加锁成功(a线程解锁的时候不能释放redis锁)，10s后b线程(requestId2)加锁，然后c线程(requestId1)加锁，结果是c线程可以重入，a线程执行完成后，b线程被唤醒尝试加锁最终超时，c线程正常释放锁
 */
public class RedisLockFactoryOptimizeV2 implements LockOperator {

	private static final Logger logger = LoggerFactory.getLogger(RedisLockFactoryOptimizeV2.class);
	private static final Long TRY_FALSE = -1L;
	private static final Long TRY_SUCCESS = 11L;
	private static final Long TRY_REENTER = 2L;
	private static final int EXPIRE_TIME = 16; // 锁的有效期
	private static final int DELAY_TIME = EXPIRE_TIME >> 1; // 锁的续租时间间隔
	private static final int LOCK_REENTER_COUNT = 0; // 已废弃，为了接口不变才保留
	private static RedisOperator redisOperator;
	private static ThreadLocal<ThreadTempData> tempDataThreadLocal = new ThreadLocal<>(); // 缓存一些线程私有数据
	private static ThreadLocal<DelayExecuteMessage<DelayTaskData<String>>> rerentMegThreadLocal = new ThreadLocal<>(); // 续租信息

	public RedisLockFactoryOptimizeV2(){
		//分布式锁默认使用redis实现，redis默认使用jedis操作
		String redisImplType = SpringContextInnerUtil.get(Constant.REDIS_IMPL);
		// 如果有链路跟踪
     	if(SpringContextInnerUtil.containsBean(Constant.TRACER_BEANNAME)){
			RedisOperator proxy = ExtensionLoader.getExtensionLoader(RedisOperator.class).getExtension(redisImplType);
			RedisTracer tracer = ExtensionLoader.getExtensionLoader(RedisTracer.class).getExtension(Constant.TRACER_REDIS_CLIENT_NAME);
			redisOperator = DynamicProxyInnerUtil.getProxy(proxy, new TraceHandler<RedisOperator>(tracer));
     	} else {
     		redisOperator = ExtensionLoader.getExtensionLoader(RedisOperator.class).getExtension(redisImplType);
     	}
	}

	@Override
	public boolean tryGetDistributedLock(String prefixName, String appName, String lockKey, String _requestId, int timeout, boolean timeOutThenExecBussiness) throws AddLockException {
		// 一把锁一个同步队列
		RedisLock sync = FutureTaskInnerUtil.get(lockKey, () -> new RedisLock(prefixName));
		ThreadTempData threadTempData = getThreadTempData();
		threadTempData.init(appName, lockKey, timeout);
		// 如果threadlocal里有requestId，则直接使用，否则uuid生成新的
		if(threadTempData.requestId != null){
			logger.info(createMsg("old requestId"));
		}else{
			if(StringUtils.isNotEmpty(_requestId)){
				threadTempData.requestId = _requestId;
				logger.info(createMsg("param requestId"));
			}else{
				threadTempData.requestId = UUID.randomUUID().toString();
				logger.info(createMsg("new requestId"));
			}
		}
		/**
    	 * 如果第一个请求执行时间超过了过期时间，在执行过程中锁释放；第二个请求进入并且和第一个请求参数相同；
    	 * 如果此时第一个请求执行完毕并试图删除key，会误删第二个请求的key；
    	 * 所以做了value的验证，需要保证两次请求的value不同
    	 * 如果使用methodName和参数，有可能两次请求的requestId是一样的
    	 * 如果可以直接设置成功锁，就返回1；否则判断是否是自己加的锁，是自己加的锁，则count+1；不是就返回-1
    	 */
		boolean result = false;
		try {
			result = sync.tryLock(timeout, TimeUnit.SECONDS);
		} catch(AddLockInterruptException e){
			// 加锁失败：就是因为超时没加上，两种情况：1、通过while加锁超时，会抛出AddLockInterruptException异常；或者入队后等待超时时间，被自动唤醒，此时result为false
			logger.error(e.getMessage());
			clearThreadTempData();
			logger.error(LogConstant.REDIS_LOCK + " add lock fail, clear threadlocal ok");
			throw new AddLockInterruptException(e.getMessage());
		} catch(LockRedisException e) {
			// 加锁异常：有可能是redis服务的问题，为了不阻断服务，认为加锁成功，只是不开启续租
			logger.error(e.getMessage());
			return true;
		} catch (Exception e) {
			// 加锁的各种异常
			String msg = createMsg("add lock exception");
			logger.error(msg, e);
			clearThreadTempData();
			throw new AddLockException(msg, e);
		}
		if(result){
			logger.info(createMsg("add lock success"));
			// 加锁成功，启动续租
			rerent(prefixName);
		} else {
			// 加锁失败
			String msg = createMsg("add lock time out");
			logger.error(msg);
			clearThreadTempData();
			logger.error(LogConstant.REDIS_LOCK + " add lock fail, clear threadlocal ok");
			throw new AddLockInterruptException(msg);
		}
		return result;
	}

	// 续租
	private void rerent(String prefixName) {
		// 如果是当前线程重入，message != null，不用启动真正续租; 如果是新加锁或跨线程重入，需要初始化message，启动真正续租
		DelayExecuteMessage<DelayTaskData<String>> message = rerentMegThreadLocal.get();
		if(message == null){
			preRerent(prefixName);
		}else{
			ThreadTempData threadTempData = getThreadTempData();
			String msg = LogConstant.REDIS_LOCK + " no need preRerent , appName: " + threadTempData.appName + ", message: " + JsonInnerUtil.objecttoJSon(message == null ? "" : message);
			logger.info(msg);
		}
	}

	// 续租前准备工作
	private void preRerent(String prefixName) {
		ThreadTempData threadTempData = getThreadTempData();
		// 缓存requestId、count和lockKey，用于延迟任务执行
		DelayExecuteMessage<DelayTaskData<String>> message = new DelayExecuteMessage<>();
		message.setData(new DelayTaskData<String>(threadTempData.lockKey, threadTempData.requestId));
		rerentMegThreadLocal.set(message);

		// 放入延迟任务，开始DELAY_TIME后续租
		JvmDelayMessageInnerUtil.sendDelayMessage(message, DELAY_TIME, () -> {
			try {
				doRerent(prefixName, threadTempData.appName, message);
			} catch (Exception e) {
				String msg = LogConstant.REDIS_LOCK + " preRerent error, lockKey: " + threadTempData.lockKey + ", message: " + JsonInnerUtil.objecttoJSon(message);
				logger.error(msg, e);
			}
		});
		String msg = LogConstant.REDIS_LOCK + " preRerent success, appName: " + threadTempData.appName + ", message: " + JsonInnerUtil.objecttoJSon(message == null ? "" : message);
		logger.info(msg);
	}

	private void doRerent(String prefixName, String appName, DelayExecuteMessage<DelayTaskData<String>> message) throws RerentLockException {
		Long traceId = message.getTraceId();
		// 只要traceId不为空，就一直续租，也就是说停止续租的条件是message里的traceId置为空（两种方式：主动置为空；释放锁自动置为空）
		if(traceId != null){
			DelayTaskData<String> data = message.getData();
			if(data != null){
				Object o = null;
				try{
					o = redisOperator.setNxAndResetTTL(prefixName, appName, data.data, data.requestId, EXPIRE_TIME);
				}catch(Exception e){
					// 续租时操作redis时报，则不再续租
					String msg = LogConstant.REDIS_LOCK + " rerent redis lock error, message: " + JsonInnerUtil.objecttoJSon(message);
					logger.error(msg, e);
					throw new RerentLockException(msg, e);
				}
				if(Integer.parseInt(String.valueOf(o)) == -1){
					throw new RerentLockException(LogConstant.REDIS_LOCK + " rerent redis lock error, already exist lockKey, message: " + JsonInnerUtil.objecttoJSon(message));
				}
				// 如果是重入，则续租；如果加锁失败不续租
				// 如果是新加锁成功，不续租（这种情况说明在续租的时候锁被释放了，这种情况可能是程序释放完锁，此时还没来得及清空traceId就到了任务执行时间，这时导致新加锁，但只要不开启续租，锁过期后还是会被释放）
				if(Integer.parseInt(String.valueOf(o)) == 2){
					JvmDelayMessageInnerUtil.sendDelayMessage(message, DELAY_TIME, () -> {
						try {
							doRerent(prefixName, appName, message);
						} catch (Exception e) {
							String msg = LogConstant.REDIS_LOCK + " rerent redis lock error, message: " + JsonInnerUtil.objecttoJSon(message);
							logger.error(msg, e);
						}
					});
				}
				String msg = LogConstant.REDIS_LOCK + " doRerent redis lock, message: " + JsonInnerUtil.objecttoJSon(message);
				// 引发续租是一个不太正常的现象，需要关注
				logger.warn(msg);
			}
		}
	}

	@Override
	public boolean releaseDistributedLock(String prefixName, String appName, String lockKey, Boolean release, Boolean forceRelease) throws ReleaseLockException, UnrerentLockException {
		RedisLock sync = FutureTaskInnerUtil.get(lockKey, () -> new RedisLock(prefixName));
		boolean result = false;
		try {
			result = sync.unlock(release);
		} catch(IllegalMonitorStateException e){
			logger.error(e.getMessage());
		} catch(IllegalArgumentException e){
			logger.error(e.getMessage());
		} catch(LockRedisException | ReleaseLockNullException e) {
			// LockRedisException: 有可能是redis服务的问题，为了不阻断服务，认为解锁成功
			// ReleaseLockNullException: 释放锁的时候，redis里的key对应的value为空，说明可能是其他jvm释放了redis锁
			clearAndUnRerent();
			logger.error(e.getMessage());
			return true;
		} catch (Exception e) {
			// 异常，停止续租
			clearAndUnRerent();
			String msg = LogConstant.REDIS_LOCK + " release redis lock error, lockKey: " + lockKey + ", appName: " + appName + ", release: " + release;
			logger.error(msg, e);
			logger.error(e.getMessage());
			throw new ReleaseLockException(msg, e);
		}
		if(result){
			// 解锁成功，停止续租
			clearAndUnRerent();
			logger.info(LogConstant.REDIS_LOCK + " release redis lock sucess, lockKey: " + lockKey + ", appName: " + appName + ", release: " + release);
		}
		return result;
	}

	// 清除thread，并停止续租
	private void clearAndUnRerent() throws UnrerentLockException {
		ThreadTempData threadTempData = getThreadTempData();
		String appName = threadTempData.appName;
		clearThreadTempData();
		try{
			unRerent(appName, threadTempData.lockKey);
		} catch(UnrerentLockException e) {
			throw new UnrerentLockException(LogConstant.REDIS_LOCK + " unRerent error: ", e);
		}
	}

	private boolean unRerent(String appName, String lockKey) throws UnrerentLockException {
		try{
			// 需要清除threadIdThread；或者是手动调用（跨线程加解锁）
			DelayExecuteMessage<DelayTaskData<String>> message = rerentMegThreadLocal.get();
			if(message == null){
				return true;
			}
			boolean result = JvmDelayMessageInnerUtil.cancelTask(message.getTraceId());
			String msg = LogConstant.REDIS_LOCK + " unRerent success, result: " + result + ", appName: " + appName + ", message: " + JsonInnerUtil.objecttoJSon(message);
    		logger.info(msg);
    		message.setTraceId(null);
    		message.setData(null);
    		rerentMegThreadLocal.remove();
			return result;
		} catch(Exception e) {
			throw new UnrerentLockException(LogConstant.REDIS_LOCK + " unRerent error: ", e);
		}
	}

	@Override
	public TwoTuple<String, Integer> getLockRequestIdAndCount() {
		ThreadTempData threadTempData = getThreadTempData();
  		return Tuple.tuple(threadTempData.requestId, LOCK_REENTER_COUNT);
	}
	
	@Override
	public TwoTuple<String, Integer> getLockRequestIdAndCount(String lockKey) {
		ThreadTempData threadTempData = getThreadTempData();
		return Tuple.tuple(threadTempData.requestId, LOCK_REENTER_COUNT);
	}

	private static ThreadTempData getThreadTempData() {
		if(tempDataThreadLocal.get() == null){
			tempDataThreadLocal.set(new ThreadTempData());
		}
		return tempDataThreadLocal.get();
	}

	private static void clearThreadTempData() {
		ThreadTempData threadTempData = tempDataThreadLocal.get();
		if(threadTempData != null){
			threadTempData.clearTempData();
			tempDataThreadLocal.remove();
		}
	}

	static class RedisLock {

		private final Sync sync;

		public RedisLock(String prefixName) {
	        sync = new Sync(prefixName);
	    }

		public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
			return sync.tryAcquireNanos(1, unit.toNanos(timeout));
		}

		/**
		 * @param release
		 * @return first: 是否解锁成功；second: 是否停止续租
		 */
		public boolean unlock(Boolean release) {
			ThreadTempData threadTempData = getThreadTempData();
			if(release == null){
				release = true;
			}
			threadTempData.release = release;
			return sync.release(1);
		}

		static class Sync extends AbstractQueuedSynchronizer {

			private static final long serialVersionUID = 3401565559846043718L;
			private AtomicBoolean tryLock = new AtomicBoolean(false); // 是否有线程在尝试加锁
			private String prefixName;
			
			public Sync(String prefixName) {
				this.prefixName = prefixName;
			}
			protected final boolean tryAcquire(int acquires) {
	            return nonfairTryAcquire(acquires);
	        }

			final boolean nonfairTryAcquire(int acquires) {
            	ThreadTempData threadTempData = tempDataThreadLocal.get();
            	final Thread current = Thread.currentThread();
            	Object redisResult = null;
            	int c = getState();
            	if (c == 0) {
            		// 还没有线程加锁成功，开始加锁
            		logger.info(createMsg("now jvm no thread success add lock"));
            		// 通过tryLock可以知道当前jvm是第一次有线程尝试加锁
	                if (tryLock.compareAndSet(false, true)) {
	                	// 只有一个线程可以获取尝试加锁的机会
	                	logger.info(createMsg("cas tryLock success, state: " + getState()));
		            	try{
		            		// 加redis锁，这里要用一开始的请求加锁时间(threadTempData.startTime)，不要取当前时间，不然当节点被唤醒后，再次尝试加锁可能还加不上，会继续等待timeout时间，变成2倍timeout时间才会被返回加锁失败
		            		long start = threadTempData.startTime;
	                		while(TRY_FALSE.equals(redisResult = redisOperator.setNxAndResetTTL(prefixName, threadTempData.appName, threadTempData.lockKey, threadTempData.requestId, EXPIRE_TIME))) {
	                			if(getState() == 0){
	                				// 还没有线程加锁成功，继续尝试加锁
		                			if((System.currentTimeMillis() - start) < threadTempData.timeout * 1000){
		            	    			try {
		            						Thread.sleep(200);
		            					} catch (InterruptedException e) {
		            						// no-op
		            					}
		            		    	}else{
		            		    		// 超时了，加锁失败，唤醒后面的线程
		            		    		String msg = createMsg("add lock time out, state: " + getState());
		            		    		logger.error(msg);
		            		    		tryLock.set(false);
		            		    		release(acquires);
		            		    		throw new AddLockInterruptException(msg);
		            		    	}
	                			} else {
	            	    			// 在等待的过程中有线程加锁成功了，所以直接入队等待就行了
	            	    			logger.info(createMsg("in loop, another thread add lock sucess, into queue, state: " + getState()));
	            	    			return false;
	            	    		}
	            			}
		        		} catch(AddLockInterruptException e) {
		        			// 超时了，加锁失败，唤醒后面的线程
							throw new AddLockInterruptException(e.getMessage(), e);
		        		} catch(Exception e) {
		        			// redis操作异常，认为加锁成功，退化为jvm锁
		        			String msg = createMsg("add lock redis error, state: " + getState());
		        			if (compareAndSetState(0, acquires)) {
			                	logger.info(createMsg("cas success, state: " + getState()));
			                	setExclusiveOwnerThread(current);
			                }
							throw new LockRedisException(msg, e);
		        		}
		            	if(redisResult == null){
		            		// 没有得到结果，认为加锁成功，退化为jvm锁
		            		String msg = createMsg("add lock result is null, state: " + getState());
		            		if (compareAndSetState(0, acquires)) {
			                	logger.info(createMsg("cas success, state: " + getState()));
			                	setExclusiveOwnerThread(current);
			                }
							throw new LockRedisException(msg);
		                }
		        		try{
		        			Integer.parseInt(redisResult.toString());
		                }catch(Exception e){
		                	// redis返回值不能正常转换为数字，认为加锁成功，退化为jvm锁
		                	String msg = createMsg("add lock result parseint error, state: " + getState());
		                	if (compareAndSetState(0, acquires)) {
			                	logger.info(createMsg("cas success, state: " + getState()));
			                	setExclusiveOwnerThread(current);
			                }
							throw new LockRedisException(msg);
		                }
		        		// 到这里说明redis加锁成功或者跨线程重入成功，尝试cas修改state
		        		if (compareAndSetState(0, acquires)) {
		                	logger.info(createMsg("cas success, state: " + getState()));
			        		// 到这里一定是加锁成功或者跨线程重入成功
		                	setExclusiveOwnerThread(current);
		        			return true;
		                }
	            	} else if(getExclusiveOwnerThread() == null) {
	            		// 到这里说明有线程在尝试加锁，但是还没有成功，则本线程尝试加锁
	            		redisResult = redisOperator.setNxAndResetTTL(prefixName, threadTempData.appName, threadTempData.lockKey, threadTempData.requestId, EXPIRE_TIME);
	            		if(TRY_SUCCESS.equals(redisResult) || TRY_REENTER.equals(redisResult)) {
	            			// 加锁或重入成功
	            			if (compareAndSetState(0, acquires)) {
			                	logger.info(createMsg("race to control cas success, state: " + getState()));
				        		// 到这里一定是加锁成功或者跨线程重入成功
			                	setExclusiveOwnerThread(current);
			        			return true;
			                }
	            		}
	            	}
            	} else if (current == getExclusiveOwnerThread()) {
            		// 到这里说明当前线程加锁成功，并且重入
            		int nextc = c + acquires; // nextc: jvm锁的总重入次数
	                if (nextc < 0) // overflow
	                    throw new Error("Maximum lock count exceeded, nextc: " + nextc);
	                setState(nextc);
	                logger.info(createMsg("reenter sucess, state: " + getState()));
	                return true;
        		}
        		// c != 0只可能是其它线程加锁或重入成功，或者在自旋等待锁，那这里就可以直接入队了
            	logger.info(createMsg("into queue, state: " + getState()));
	            return false;
	        }

			// 释放锁的操作在单个jvm里就是单线程操作
			protected final boolean tryRelease(int releases) {
				// 如果释放锁时发现还没有获取锁的线程，帮忙唤醒排队的线程
				if(getExclusiveOwnerThread() == null){
					logger.info(createMsg("release lock ownerThread is null, state: " + getState()));
					return true;
				}
				int c = getState() - releases;
				if(c < 0){
					c = 0;
				}
				// 释放锁必须保证当前线程是锁的持有者
		        if (Thread.currentThread() != getExclusiveOwnerThread())
		        	throw new IllegalMonitorStateException(createMsg("release lock IllegalMonitorStateException, currentThread: " + Thread.currentThread() + ", ownerThread : " + getExclusiveOwnerThread() + ", state: " + getState()));
		        try{
					ThreadTempData threadTempData = getThreadTempData();
					if(threadTempData.requestId == null){
						String msg = createMsg("release lock no threadlocal");
						logger.error(msg);
						throw new IllegalArgumentException(msg);
					}
					if(c == 0){
						if(threadTempData.release) {
							String script =  "if redis.call('get', KEYS[1]) == ARGV[1] "
										   + "then redis.call('del', KEYS[1]); return 0 "
										   + "else return redis.call('get', KEYS[1]) "
										   + "end";
							doRelease(threadTempData, script);
			        		// 彻底释放
			        		logger.info(createMsg("release lock success"));
						} else {
							String script =  "if redis.call('get', KEYS[1]) == ARGV[1] "
									   + "then return 0 "
									   + "else return redis.call('get', KEYS[1]) "
									   + "end";
							doRelease(threadTempData, script);
							// 不释放redis锁
							logger.info(createMsg("release lock success, but not release redis"));
						}
						return true;
					} else {
						String script =  "if redis.call('get', KEYS[1]) == ARGV[1] "
								   	   + "then return 1 "
								   	   + "else return redis.call('get', KEYS[1]) "
								   	   + "end";
						doRelease(threadTempData, script);
		        		// 减少重入次数
		        		logger.info(createMsg("release lock one time"));
		        		return false;
		        	}
				} finally {
					setState(c);
					if(c == 0) {
						logger.info(createMsg("release, state: " + getState() + ", ownerThread: " + getExclusiveOwnerThread()));
						setExclusiveOwnerThread(null);
					}else{
						logger.info(createMsg("minus state: " + getState()));
					}
				}
			}

			@SuppressWarnings({ "unchecked", "serial", "rawtypes" })
			private void doRelease(ThreadTempData threadTempData, String script) {
				Object redisResult = null; // redis返回结果
				try{
					redisResult = redisOperator.eval(prefixName, threadTempData.appName, script, Collections.singletonList(threadTempData.lockKey), new ArrayList(){{add(threadTempData.requestId); }});
				} catch(Exception e) {
					// 释放锁时，操作redis失败
					String msg = createMsg("release lock redis error");
					throw new LockRedisException(msg, e);
				}
				if(redisResult == null){
					// 释放锁的时候，redis里的key对应的value为空，说明可能是其他jvm释放了redis锁
					String msg = createMsg("release lock result is null");
					logger.info(msg);
					throw new ReleaseLockNullException(msg);
				}
				if(redisResult instanceof String){
					// 不是自己加的锁，解锁失败
					String msg = createMsg("release lock not myself lock, redis lockKey: " + redisResult);
					logger.error(msg);
					throw new RuntimeException(msg);
				}
				try{
					Integer.parseInt(redisResult.toString());
				}catch(Exception e){
					// redis返回值不能正常转换为数字，解锁失败
					String msg = createMsg("release lock result parseint error");
					logger.error(msg);
					throw new RuntimeException(msg);
				}
			}
		}
	}

	// 线程临时保存的对象
	static class ThreadTempData {
		public String lockKey;
		public String requestId;
		public String appName;
		public int timeout;
		public long startTime; // 开始加锁时间
		public boolean release = false;

		public void init(String appName, String lockKey, int timeout) {
			this.appName = appName;
			this.lockKey = lockKey;
			this.timeout = timeout < 0 ? 0 : timeout;
			startTime = System.currentTimeMillis(); // 开始加锁时间
		}
		public void clearTempData() {
			lockKey = null;
			requestId = null;
			appName = null;
			timeout = 0;
		}
	}

	// 续租对象
	static class DelayTaskData<T> implements Serializable {
		public static final long serialVersionUID = 6586594134752070186L;
		public T data;
		public String requestId;

		public DelayTaskData() {
			super();
		}
		public DelayTaskData(T data, String requestId) {
			super();
			this.data = data;
			this.requestId = requestId;
		}
	}

	private static String createMsg(String msg) {
		ThreadTempData threadTempData = tempDataThreadLocal.get();
		return LogConstant.REDIS_LOCK + " " + msg + ", lockKey: " + StringUtils.defaultString(threadTempData.lockKey) + ", timeout: " + threadTempData.timeout + ", appName: " + StringUtils.defaultString(threadTempData.appName) + ", requestId: " + StringUtils.defaultString(threadTempData.requestId);
	}

}